#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>


#define NUM 1000
#define COMMANDCONUT 64
char LineCommand[NUM]; 
char *CommandExec[COMMANDCONUT];
// 真正的shell就是用子进程去执行各种命令

#define NO 0
//输入
#define INPUT 1
#define OUTPUT 2
#define OUTPUT_APPEND 3

// 这是重定向方式
int remdirTypes = 0;
char * file = NULL;

void DeleteSpace(char ** start)
{
   while( **start == ' ')
   {
      (*start)++;
   }
}


void CheckCommand(char * str)
{
  // 学习点一 ，str可以这样去操作，在真真的项目中，也是这样的
  // 学习点二， 空格和 "\0"完全是两码事
  assert(str);
  char * start = str;
  char * end = str + strlen(str);

  while(start < end)
  {
     if( *start == '<' )
     {
        *start = 0; // 直接写为零，开始去空格
        start++;
        DeleteSpace(&start);   
        remdirTypes = INPUT;
        file = start;
     }
    else if(*start == '>' )
    {
       *start = 0; 
       start++;
       if(*start == '>')
       {
          *start = 0;
          start++;
          remdirTypes = OUTPUT_APPEND; 
       }
       else{
          remdirTypes = OUTPUT;
      }
      DeleteSpace(&start);
      file = start;

    }
     else{
       start++;
     }
  }
}

int main()
{
   while(1)
   {
      // 要刷新全局变量
      remdirTypes = 0;
      file = NULL;


      printf("[%s@云服务器]:",getenv("USER"));
      fflush(stdout);
     // Line 表示缓冲区
      char *s = fgets(LineCommand,sizeof(LineCommand)-1,stdin);  
      assert(s!=NULL);
      // 这里要删一个换行符
      (void)s; 
      LineCommand[strlen(LineCommand)-1] =  0; 

      printf("Command:%s\n",LineCommand);

      // ls > file    
    
     CheckCommand(LineCommand);

      // 开始分割s , 用strtok
      CommandExec[0] = strtok(LineCommand," ");
      int i = 1;
      // 有点妙的这一行，先分割写入CommandExec ，最后判读CommandExec[i]是否为空
      while(CommandExec[i++] = strtok(NULL," ") );

      //内嵌命令cd,本命令子进程调用无效
      if(strcmp(CommandExec[0], "cd") == 0 )
      {
         if(strcmp(CommandExec[1] , "..") == 0)
         {
             chdir(CommandExec[1]);
             continue;
         }
      }


      // 准备工作终于做完，现在来用子进程执行吧
      pid_t ret = fork();
      if(ret == 0)
      {
         switch(remdirTypes) 
         {
           case NO:
           break;

           case INPUT:{ 
           int fd = open(file,O_RDONLY);
           
           if(fd < 0)
           {
              printf("open");
              exit(1);
           }

           dup2(fd , 0);   // 原来是从0读入，现在改为fd 
          }
           break;

           case OUTPUT:
           case OUTPUT_APPEND:{
           umask(0);
           int flags = 0; 
           flags |= O_RDWR; 
           flags |= O_CREAT;
           if(remdirTypes == OUTPUT_APPEND)
           flags |= O_APPEND;

           int fd = open(file , flags , 0666);
           dup2(fd , 1);
           
           if(fd < 0)
           {
              perror("open");
              exit(1);
           }
          }
           break;

           default:
           printf("BUG?\n");
           break;

         }


         execvp(CommandExec[0],CommandExec);  // 被替换上来的进程不会影响重定向.
         exit(-1);
      }

      pid_t isSuccess = waitpid(ret , NULL , 0);
      if(isSuccess == ret)
      {
         printf("执行完毕\n");
      }

   }
   return 0;
}
